import re
import struct
from typing import Callable

from apps.tcpserver.proto.utils import fill_data, btoi, split_body
from ..connection import Message


class Field:
    # 后面考虑重构
    type: int or str
    length: int
    data: bytes or str or int

    def __init__(
            self,
            bytesorder: str = 'big',
            length: int = 0,
            order: int = 1,
            default: str or bytes = b'',
            parser: Callable = None,
            signed: bool = True,
            doc: str = '',

    ):
        self.type = bytes
        self.length = length
        self.doc = doc
        self.order = order
        self.signed = signed
        self.default = default
        self.bytesorder = bytesorder
        self.parser = parser

    def get_data(self, items=None, key=None) -> bytes:
        return self.data

    def reverse_data(self) -> bytes or int or str:
        return bytes(self.data)


class FloatField(Field):
    def __init__(self, *args, **kwargs):
        super(FloatField, self).__init__(
            *args,
            **kwargs
        )
        self.type = float

    def reverse_data(self) -> bytes or int or str:
        res = self.data
        res = struct.unpack(
            f'{"<" if self.bytesorder == "little" else ">"}f', self.data)[0]

        return res

    def get_data(self, items=None, key=None) -> bytes:
        if isinstance(self.data, bytes):
            return fill_data(self.data, width=self.length, left=self.bytesorder == 'big')
        data = struct.pack(
            f'{"<" if self.bytesorder == "little" else ">"}f', self.type(self.data))
        return fill_data(data, width=self.length, left=False)


class IntegerField(Field):
    def __init__(self, *args, **kwargs):
        super(IntegerField, self).__init__(
            *args,
            **kwargs
        )
        self.type = int

    def reverse_data(self) -> bytes or int or str:
        res = self.data
        res = btoi(self.data, self.bytesorder, signed=self.signed)
        return res

    def get_data(self, items=None, key=None) -> bytes:
        if isinstance(self.data, bytes):
            return fill_data(
                self.data,
                width=self.length,
                left=self.bytesorder == 'big'
            )
        data = fill_data(
            self.type(self.data),
            width=self.length,
            left=self.bytesorder == 'little'
        )
        print(data.hex())
        if len(data) > self.length:
            data = data[len(data) - self.length:]

        return data


class StringField(Field):

    def __init__(self, *args, **kwargs):
        super(StringField, self).__init__(
            *args,
            **kwargs
        )
        self.type = str

    def reverse_data(self) -> bytes or int or str:
        res = self.data

        try:
            res = re.sub(r'^\x.*?\x', str(self.data, encoding='utf-8'))
        except Exception as e:
            pass
        try:
            data = self.data.split(b'\x00')[0]
            res = str(data, encoding='utf-8')
        except Exception as e:
            res = data.hex()

        return res

    def get_data(self, items=None, key=None) -> bytes:
        return fill_data(self.data, width=self.length, left=False)


class Serializer:
    # 考虑重构
    order = 0

    @classmethod
    def pack(cls, data, msg_type=b'', many=False, pack=True, dev=None):
        keys = [i for i in dir(cls) if not i.startswith('__')]
        fields = {
            i: getattr(cls, i) for i in keys if isinstance(getattr(cls, i), (Field, type))
        }
        res = msg_type
        if not many:
            # 判断是否是多个item
            data = [data]

        for i in data:
            for k, f in sorted(fields.items(), key=lambda i: i[1].order):
                method = getattr(cls, f'transform_{k}', None)
                data = i[k]
                data = method(data, i, dev) if method else data
                f.data = data
                print(f.get_data(i, k, ))
                res += f.get_data(i, k, )
        return Message(res) if pack else res

    @classmethod
    def unpack(cls, data):
        fields = [
            (i, getattr(cls, i))
            for i in dir(cls) if not i.startswith('__')
            if isinstance(getattr(cls, i), (Field, type))
        ]
        fields = sorted(fields, key=lambda i: i[1].order)
        res = dict()
        start = 0
        for k, f in fields:
            if f.length and isinstance(f, Field):
                f.data = data[start:start + f.length]
                res[k] = f.reverse_data()
                start += f.length
            else:

                index = fields.index((k, f))
                if index == len(fields) - 1:
                    decode_data = data[start:]
                    start = len(data)
                else:
                    offset_length = sum(
                        (i[1].length for i in fields[index + 1:]))
                    decode_data = data[start:len(data) - offset_length + 1]
                    start = len(data) - offset_length
                
                res[k] = f.unpack_subserializer(decode_data)

        return res

    @classmethod
    def length(cls):
        keys = [i for i in dir(cls) if not i.startswith('__')]
        fields = [
            getattr(cls, i) for i in keys if isinstance(getattr(cls, i), Field)
        ]
        return sum((i.length for i in fields))

    @classmethod
    def unpack_subserializer(cls, data: bytes):
        print(cls.length(), len(data) ,cls.length())
        datas = split_body(data, (cls.length() for i in range(
            len(data) // cls.length())), decode=False)
        print(datas)
        return [cls.unpack(i) for i in datas]
